summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service/filesystem/fsp/fs_i_file.cpp
blob: 7e0c90a89fdfc28a628ed64bddbf7b2df40bb2f2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/file_sys/errors.h"
#include "core/hle/service/filesystem/fsp/fs_i_file.h"
#include "core/hle/service/ipc_helpers.h"

namespace Service::FileSystem {

IFile::IFile(Core::System& system_, FileSys::VirtualFile backend_)
    : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) {
    static const FunctionInfo functions[] = {
        {0, &IFile::Read, "Read"},
        {1, &IFile::Write, "Write"},
        {2, &IFile::Flush, "Flush"},
        {3, &IFile::SetSize, "SetSize"},
        {4, &IFile::GetSize, "GetSize"},
        {5, nullptr, "OperateRange"},
        {6, nullptr, "OperateRangeWithBuffer"},
    };
    RegisterHandlers(functions);
}

void IFile::Read(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const u64 option = rp.Pop<u64>();
    const s64 offset = rp.Pop<s64>();
    const s64 length = rp.Pop<s64>();

    LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);

    // Error checking
    if (length < 0) {
        LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(FileSys::ERROR_INVALID_SIZE);
        return;
    }
    if (offset < 0) {
        LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(FileSys::ERROR_INVALID_OFFSET);
        return;
    }

    // Read the data from the Storage backend
    std::vector<u8> output = backend->ReadBytes(length, offset);

    // Write the data to memory
    ctx.WriteBuffer(output);

    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.Push(static_cast<u64>(output.size()));
}

void IFile::Write(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const u64 option = rp.Pop<u64>();
    const s64 offset = rp.Pop<s64>();
    const s64 length = rp.Pop<s64>();

    LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);

    // Error checking
    if (length < 0) {
        LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(FileSys::ERROR_INVALID_SIZE);
        return;
    }
    if (offset < 0) {
        LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(FileSys::ERROR_INVALID_OFFSET);
        return;
    }

    const auto data = ctx.ReadBuffer();

    ASSERT_MSG(static_cast<s64>(data.size()) <= length,
               "Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
               length, data.size());

    // Write the data to the Storage backend
    const auto write_size =
        static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length));
    const std::size_t written = backend->Write(data.data(), write_size, offset);

    ASSERT_MSG(static_cast<s64>(written) == length,
               "Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
               written);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IFile::Flush(HLERequestContext& ctx) {
    LOG_DEBUG(Service_FS, "called");

    // Exists for SDK compatibiltity -- No need to flush file.

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IFile::SetSize(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const u64 size = rp.Pop<u64>();
    LOG_DEBUG(Service_FS, "called, size={}", size);

    backend->Resize(size);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void IFile::GetSize(HLERequestContext& ctx) {
    const u64 size = backend->GetSize();
    LOG_DEBUG(Service_FS, "called, size={}", size);

    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.Push<u64>(size);
}

} // namespace Service::FileSystem